﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq.Expressions;

namespace ExcelFileQueryTool
{
    public enum ExcelWorkLogFilterField
    {
        None = 0,
        Name = 1,
        Item = 2,
        Driver = 4,
        Pay = 8,
        Note = 16,
        Type = 32
    }

    public enum ExcelWorkLogFilterMatchMethod
    {
        Like = 1,
        Equal = 2
    }

    public enum ExcelWorkLogFilterCombineMethod
    {
        And = 1,
        Or = 2
    }

    public sealed class ExcelWorkLogFilterFieldConfig
    {
        public ExcelWorkLogFilterField Field { get; set; }

        public string Value { get; set; }
    }

    public sealed class ExcelWorkLogFilterConfig
    {
        public ExcelWorkLogFilterConfig()
        {
            FieldConfigs = new List<ExcelWorkLogFilterFieldConfig>();
            MatchMethod = ExcelWorkLogFilterMatchMethod.Like;
            CombineMethod = ExcelWorkLogFilterCombineMethod.And;
        }

        public ExcelWorkLogFilterMatchMethod MatchMethod { get; set; }

        public ExcelWorkLogFilterCombineMethod CombineMethod { get; set; }

        public List<ExcelWorkLogFilterFieldConfig> FieldConfigs { get; private set; }

        public override int GetHashCode()
        {
            return MatchMethod.GetHashCode() ^ CombineMethod.GetHashCode() ^ FieldConfigs.GetHashCode();
        }

    }

    public interface IExcelWorkLogFilter
    {
        string GenerateKeyword(ExcelWorkLogFilterConfig config);
        IEnumerable<ExcelWorkLogItem> Apply(IEnumerable<ExcelWorkLogItem> soure, string filter);
    }

    public class SpecialKeywordExcelWorkLogFilter : IExcelWorkLogFilter
    {
        private const char FieldValueSeperator = ':';

        private const char ConfigPartSeperator = '_';

        public string GenerateKeyword(ExcelWorkLogFilterConfig config)
        {
            if (config == null || config.FieldConfigs.Count == 0)
                return string.Empty;

            string filter = config.MatchMethod.ToString() + ConfigPartSeperator + config.CombineMethod.ToString();

            foreach (var field in config.FieldConfigs)
            {
                filter += ConfigPartSeperator + field.Field.ToString() + FieldValueSeperator + field.Value;
            }

            return filter;
        }

        public IEnumerable<ExcelWorkLogItem> Apply(IEnumerable<ExcelWorkLogItem> source, string filter)
        {
            var config = ParseFilterConfig(filter);
            var type = GetFieldConfigValue(config.FieldConfigs, ExcelWorkLogFilterField.Type);
            var name = GetFieldConfigValue(config.FieldConfigs, ExcelWorkLogFilterField.Name);
            var item = GetFieldConfigValue(config.FieldConfigs, ExcelWorkLogFilterField.Item);
            var driver = GetFieldConfigValue(config.FieldConfigs, ExcelWorkLogFilterField.Driver);
            var note = GetFieldConfigValue(config.FieldConfigs, ExcelWorkLogFilterField.Note);
            var pay = GetFieldConfigValue(config.FieldConfigs, ExcelWorkLogFilterField.Pay);

            if (config.MatchMethod == ExcelWorkLogFilterMatchMethod.Equal)
            {
                if (config.CombineMethod == ExcelWorkLogFilterCombineMethod.And)
                    return QueryEqualAnd(source, type, name, item, driver, note, pay);
                else
                    return QueryEqualOr(source, type, name, item, driver, note, pay);
            }
            else
            {
                if (config.CombineMethod == ExcelWorkLogFilterCombineMethod.And)
                    return QueryLikeAnd(source, type, name, item, driver, note, pay);
                else
                    return QueryLikeOr(source, type, name, item, driver, note, pay);
            }
        }

        static string GetFieldConfigValue(IEnumerable<ExcelWorkLogFilterFieldConfig> config, ExcelWorkLogFilterField field)
        {
            var fieldCfg = config.FirstOrDefault(x => x.Field == field);
            return fieldCfg == null ? null : fieldCfg.Value;
        }

        static IEnumerable<ExcelWorkLogItem> QueryLikeOr(IEnumerable<ExcelWorkLogItem> source, string type, string name, string item, string driver, string note, string pay)
        {
            // All fields in ExcelWorkLogItem not null
            return source.Where(x =>
               (string.IsNullOrEmpty(type) == false && x.类型.Contains(type))
               || (string.IsNullOrEmpty(name) == false && x.客户供应商.Contains(name))
               || (string.IsNullOrEmpty(item) == false && x.货物.Contains(item))
               || (string.IsNullOrEmpty(driver) == false && x.运输方式.Contains(driver))
               || (string.IsNullOrEmpty(note) == false && x.备注.Contains(note))
               || (string.IsNullOrEmpty(pay) == false && x.收付款人.Contains(pay))
            );
        }

        static IEnumerable<ExcelWorkLogItem> QueryLikeAnd(IEnumerable<ExcelWorkLogItem> source, string type, string name, string item, string driver, string note, string pay)
        {
            // All fields in ExcelWorkLogItem not null
            return source.Where(x =>
               (string.IsNullOrEmpty(type) || x.类型.Contains(type))
               && (string.IsNullOrEmpty(name) || x.客户供应商.Contains(name))
               && (string.IsNullOrEmpty(item) || x.货物.Contains(item))
               && (string.IsNullOrEmpty(driver) || x.运输方式.Contains(driver))
               && (string.IsNullOrEmpty(note) || x.备注.Contains(note))
               && (string.IsNullOrEmpty(pay) || x.收付款人.Contains(pay))
            );
        }

        static IEnumerable<ExcelWorkLogItem> QueryEqualOr(IEnumerable<ExcelWorkLogItem> source, string type, string name, string item, string driver, string note, string pay)
        {
            // All fields in ExcelWorkLogItem not null
            return source.Where(x =>
               (string.IsNullOrEmpty(type) == false && x.类型 == type)
               || (string.IsNullOrEmpty(name) == false && x.客户供应商 == name)
               || (string.IsNullOrEmpty(item) == false && x.货物 == item)
               || (string.IsNullOrEmpty(driver) == false && x.运输方式 == driver)
               || (string.IsNullOrEmpty(note) == false && x.备注 == note)
               || (string.IsNullOrEmpty(pay) == false && x.收付款人 == pay)
            );
        }

        static IEnumerable<ExcelWorkLogItem> QueryEqualAnd(IEnumerable<ExcelWorkLogItem> source, string type, string name, string item, string driver, string note, string pay)
        {
            // All fields in ExcelWorkLogItem not null
            return source.Where(x =>
               (string.IsNullOrEmpty(type) || x.类型 == type)
               && (string.IsNullOrEmpty(name) || x.客户供应商 == name)
               && (string.IsNullOrEmpty(item) || x.货物 == item)
               && (string.IsNullOrEmpty(driver) || x.运输方式 == driver)
               && (string.IsNullOrEmpty(note) || x.备注 == note)
               && (string.IsNullOrEmpty(pay) || x.收付款人 == pay)
            );
        }

        public static ExcelWorkLogFilterConfig ParseFilterConfig(string filter)
        {
            var config = new ExcelWorkLogFilterConfig();

            if (string.IsNullOrEmpty(filter) == false)
            {
                var configArr = filter.Split(new char[1] { ConfigPartSeperator }, StringSplitOptions.RemoveEmptyEntries);

                config.MatchMethod = (ExcelWorkLogFilterMatchMethod)Enum.Parse(typeof(ExcelWorkLogFilterMatchMethod), configArr[0]);
                config.CombineMethod = (ExcelWorkLogFilterCombineMethod)Enum.Parse(typeof(ExcelWorkLogFilterCombineMethod), configArr[1]);

                for (int i = 2; i < configArr.Length; i++)
                {
                    var fieldArr = configArr[i].Split(new char[1] { FieldValueSeperator }, StringSplitOptions.RemoveEmptyEntries);
                    config.FieldConfigs.Add(new ExcelWorkLogFilterFieldConfig
                    {
                        Field = (ExcelWorkLogFilterField)Enum.Parse(typeof(ExcelWorkLogFilterField), fieldArr[0]),
                        Value = fieldArr[1]
                    });
                }

            }

            return config;
        }
    }

}
